/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include "ssi_functionset.h"
#include "ssi_interpreter.h"
#include "ssi_variable.h"
#include "ssi_function.h"

#include "ssi_datavoid.h"
#include "ssi_functionset.h"


CFunctionSet::CFunctionSet(void)
{
}

CFunctionSet::~CFunctionSet(void)
{
  int i = 0;
}

CStatus CFunctionSet::AddFunction(CFunctionDecl& aFunc)
{
	aFunc.Package = CurrPackage;
	if(!aFunc.ReturnType){
		aFunc.ReturnType = CDataVoid::GetRegisteredType();
	}

	CFunctionDecl Temp = aFunc;
	FuncNode* aFuncPtr = SSI_NULL;
	SSI_INT64 NumParams;
	SSI_INT64 ctr;
	SSI_INT64 SameParamCtr = 0;

	while(aFuncPtr = FindFunctionDecl(Temp, aFuncPtr)){
		aFuncPtr->Get(Temp);
    SameParamCtr = 0;
		NumParams = aFunc.getNumParams();
		if(Temp.getNumParams() == NumParams){
			for(ctr = 0; ctr < NumParams; ctr++){
				if(Temp.getParamType(ctr) == aFunc.getParamType(ctr)){
					SameParamCtr++;
				}else{
				}
			}
			if(SameParamCtr == NumParams){
				String sErr;
				sErr.Format(ERR02004, Temp.Name.Str());
				return CStatus(sERROR, sErr);
			}
		}
	}
	//aFuncPtr = Funcs.push(aFunc);
  aFuncPtr = Funcs.add_sort(aFunc);
	if(aFuncPtr){
		AddPackage(CurrPackage);
		return STAT_OK;
	}
	return STAT_ERR;  // Internal Error  KEEP

	/*
	if(aFuncPtr = FindFunctionDecl(Temp, 0)){//Funcs.find(Temp)){
		aFuncPtr->Get(Temp);
		if(Temp.bIsDefined){
			// Function already defined
			String sErr;
			sErr.Format(ERR02004, Temp.Name.Str());
			return CStatus(sERROR, sErr);
		}
		aFuncPtr->Set(aFunc);
		return STAT_OK;
	}else{
		aFuncPtr = Funcs.push(Temp);
		if(aFuncPtr) return STAT_OK;
	}
	String sErr;
	sErr.Format(ERR02005, Temp.Name.Str());
  return CStatus(sERROR, sErr);
	*/
}


FuncNode* CFunctionSet::FindFunctionDecl(CFunctionDecl &aFunc, FuncNode* In)
{
	FuncNode* aFuncPtr;
	if(In){
		aFuncPtr = In->next();
		if(!aFuncPtr) return 0;  // This prevents an infinite loop in a for statement...
		//  Really, if it gets to the end of the list, and still hasnt found it, return 0...
	}else{
		aFuncPtr = 0;
	}
  if(aFuncPtr = Funcs.find(aFunc, aFuncPtr))
  {
    aFuncPtr->Get(aFunc);
    return aFuncPtr;
  }
	return 0;
}

void CFunctionSet::ClearPackage(String &sPackage)
{
	FuncNode* aFuncPtr;
  FuncNode* ClearPtr;
  aFuncPtr = Funcs.getTop();
  CFunctionDecl aDecl;
  while(aFuncPtr){
    aFuncPtr->Get(aDecl);
    if(aDecl.Package == sPackage){
      ClearPtr = aFuncPtr;
      aFuncPtr = aFuncPtr->next();  // I think we want this in both places
      Funcs.clear_range(ClearPtr, ClearPtr);
      ClearPtr = SSI_NULL;
    }else{
      aFuncPtr = aFuncPtr->next();
    }
    
  }
}

CStatus CFunctionSet::CheckFunction(CFunction& InFunc, SSI_FunctionAPI &theAPI, CFunctionDecl& theDecl)
{
  CStatus RetStat;
  String RetMess;
	String ParamTypes;
  SSI_INT64 ctr = 0;
  SSI_INT64 NumParams = 0;
	SSI_BOOL bFoundFunction = 0;
	SSI_BOOL bOK = 0;

	CVariable* Vars = 0;

  theDecl.Name = InFunc.Name;

	FuncNode* aNode = 0;
	while(aNode = FindFunctionDecl(theDecl, aNode)){
		bFoundFunction = 1;
		NumParams = theDecl.getNumParams();
		if(InFunc.GetNumParams() == NumParams){
			bOK = 1;
			theAPI.InitParams(NumParams);
			theAPI.m_FunctionName = theDecl.Name;
			Vars = new CVariable[NumParams];

			for(ctr = 0; ctr < NumParams; ctr++){
		    Vars[ctr] = InFunc.GetVariable(ctr);
				if((Vars[ctr].GetType() != theDecl.getParamType(ctr)) && (theDecl.getParamType(ctr) != dANY)){
					bOK = 0;

					// Dont do this here!!
					//delete [] Vars;
					//delete [] theAPI.Parameters;
					//theAPI.Parameters = SSI_NULL;
					//return RetStat;
				}else{
					theAPI.AddParameter(Vars[ctr], ctr);
					//theAPI.Parameters[ctr] = &Vars[ctr];
				}
			}
			if(bOK){
				delete [] Vars;
				theAPI.RetVar.SetType(theDecl.ReturnType);
				return STAT_OK;
			}else{
				delete [] Vars;

				theAPI.ClearParameters();
			}
			// else continue
		}
	}

	if(!bFoundFunction){
		RetMess.Format("Function '%s' not found", theDecl.Name.Str());
		RetStat.Set(sERROR, RetMess);
		return RetStat;
	}else{
		NumParams = InFunc.GetNumParams();

		Vars = new CVariable[NumParams+1];

		for(ctr = 0; ctr < NumParams; ctr++){
	    Vars[ctr] = InFunc.GetVariable(ctr);
			ParamTypes = ParamTypes + Vars[ctr].GetTypeString();
			ParamTypes = ParamTypes + ", ";
		}
		ParamTypes.chop();
		ParamTypes.chop();

		delete [] Vars;

		if(NumParams <= 0){
			ParamTypes = "none";
		}

		MAKEERR2(ERR01108, theDecl.Name.Str(), ParamTypes.Str());
	}
}

CStatus CFunctionSet::SetPackageName(const char* Name)
{
	CurrPackage = Name;
	return STAT_OK;
}

SSI_BOOL CFunctionSet::IsFunction(CFunctionDecl& InDecl)
{
	if(FindFunctionDecl(InDecl, 0)){
		return 1;
	}
	return 0;
}

int CFunctionSet::AddPackage(String& In)
{
	StringNode* aNode;
	String s = In;
	aNode = Packages.find(s);

	if(aNode) return 0;

	aNode = Packages.push(s);
	if(!aNode){
		return 0;
	}
	return 1;
}

CFunctionSet& CFunctionSet::operator =(CFunctionSet &Other)
{
  Funcs.Clear();
  Packages.Clear();
  Funcs = Other.Funcs;
  Packages = Other.Packages;
  CurrPackage = Other.CurrPackage;
	return *this;
}




